package org.ns.framework.datasource.redis;

import org.ns.basics.commons.reflect.FieldUtils;
import org.ns.framework.datasource.anns.DBSource;
import org.ns.framework.datasource.config.Redis;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class RedisBeanInjector implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        Class<?> targetClass = AopUtils.getTargetClass(bean);

        List<Field> declaredFields = findFields(targetClass);

        for (Field declaredField : declaredFields) {

            injector(declaredField, bean);
        }

        return bean;
    }

    private List<Field> findFields (Class<?> targetClass) {

        List<Field> fields = new ArrayList<>();

        Class<?> nextClass = targetClass;

        while (nextClass != null && nextClass != Object.class) {
            Field[] declaredFields = nextClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Class<?> fieldType = declaredField.getType();
                if (fieldType == Redis.class && AnnotatedElementUtils.hasAnnotation(declaredField, DBSource.class)) {
                    fields.add(declaredField);
                }
            }

            nextClass = nextClass.getSuperclass();
        }
        return fields;
    }

    private void injector(Field field, Object bean) {
        // Class<?> fieldType = field.getType();
        DBSource dbSource = AnnotatedElementUtils.findMergedAnnotation(field, DBSource.class);
        String datasourceName = dbSource.value();
        Redis redis = applicationContext.getBean(datasourceName, Redis.class);
        FieldUtils.set(bean, field, redis);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
